package com.rnkrsoft.suyan.config.server.ajax.executor;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.rnkrsoft.logtrace4j.ErrorContextFactory;
import com.rnkrsoft.reflection4j.extension.ExtensionLoader;
import com.rnkrsoft.suyan.config.server.ajax.authority.AuthorityAuthenticator;
import com.rnkrsoft.suyan.config.server.ajax.authority.AuthorityContext;
import com.rnkrsoft.suyan.config.server.ajax.authority.AuthorityExtractor;
import com.rnkrsoft.suyan.config.server.ajax.config.AjaxConfig;
import com.rnkrsoft.suyan.config.server.ajax.data.AjaxRequest;
import com.rnkrsoft.suyan.config.server.ajax.data.AjaxResponse;
import com.rnkrsoft.suyan.config.server.ajax.session.LoginUserId;
import com.rnkrsoft.suyan.config.server.ajax.session.LoginUserName;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.doc.DocScanner;
import javax.doc.InterfaceInfo;
import javax.doc.data.IRequest;
import javax.doc.data.IResponse;
import javax.doc.runtime.RuntimeDocScanner;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;

@Slf4j
abstract class AbstractAjaxExecutor implements AjaxExecutor {
    protected DocScanner docScanner;
    @Getter
    protected AjaxConfig ajaxConfig;
    protected Gson gson = new GsonBuilder().create();

    public AbstractAjaxExecutor(AjaxConfig ajaxConfig) {
        this.ajaxConfig = ajaxConfig;
        this.docScanner = new RuntimeDocScanner();
        this.docScanner.addPackage(ajaxConfig.getServiceBasePackage());
        try {
            this.docScanner.init(true);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取请求的HTTP BODY内容
     *
     * @param request
     * @return
     */
    String readHttpBody(HttpServletRequest request) {
        String method = request.getMethod().toUpperCase();
        if (method.equals("POST")){
            BufferedReader br = null;
            StringBuilder sb = new StringBuilder();
            try {
                br = request.getReader();
                String str;
                while ((str = br.readLine()) != null) {
                    sb.append(str);
                }
                br.close();
            } catch (IOException e) {
                return null;
            } finally {
                if (null != br) {
                    try {
                        br.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return sb.toString();
        }else{
            throw new IllegalArgumentException("不支持的method");
        }

    }

    /**
     * 调用权限服务检查调用的接口是否拥有权限
     *
     * @param authorityContext 权限上下文
     * @return 是否拥有权限
     */
    boolean hasAuthority(AuthorityContext authorityContext) {
        AuthorityAuthenticator authorityAuthenticator = ExtensionLoader.getExtensionLoader(AuthorityAuthenticator.class).getExtension(ajaxConfig.getAuthorityAuthenticatorName());
        return authorityAuthenticator.hasAuthority(authorityContext);
    }


    @Override
    public void execute(HttpServletRequest request, HttpServletResponse response) {
        try {
            request.setCharacterEncoding("UTF-8");
            response.setHeader("content-type", "application/json;charset=UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("happens error!", e);
            return;
        }
        String requestJson = readHttpBody(request);
        AuthorityExtractor extractor = ExtensionLoader.getExtensionLoader(AuthorityExtractor.class).getExtension(ajaxConfig.getAuthorityExtractorName());
        AuthorityContext authorityContext = extractor.extract(request, response);
        String responseJson = null;
        if (authorityContext.isTermination()) {
            AjaxResponse ajaxResponse = AjaxResponse.builder()
                    .code(authorityContext.getCode(), authorityContext.getDesc())
                    .build();
            responseJson = gson.toJson(ajaxResponse);
        } else {
            responseJson = execute(requestJson, authorityContext);
        }
        Writer writer = null;
        try {
            writer = response.getWriter();
            writer.append(responseJson);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.flush();
                } catch (IOException e) {
                    ;
                }
                writer = null;
            }
        }
    }

    @Override
    public String execute(String request, AuthorityContext authorityContext) {
        AjaxRequest ajaxRequest = gson.fromJson(request, AjaxRequest.class);
        if (ajaxRequest == null){
            AjaxResponse ajaxResponse = AjaxResponse.builder()
                    .failure()
                    .build();
            return gson.toJson(ajaxResponse);
        }
        String bizRequestJson = ajaxRequest.getData();
        String product = ajaxRequest.getProduct();
        String action = ajaxRequest.getAction();
        String version = ajaxRequest.getVersion();
        String operate = ajaxRequest.getOperate();
        authorityContext.setProduct(product);
        authorityContext.setAction(action);
        authorityContext.setVersion(version);
        authorityContext.setOperate(operate);

        if (!hasAuthority(authorityContext)) {
            AjaxResponse ajaxResponse = AjaxResponse.builder()
                    .noAuthority()
                    .build();
            return gson.toJson(ajaxResponse);
        }
        String packageName = ajaxConfig.getServiceBasePackage() + "." + product + ".services" + ".";
        String serviceClassName = packageName + action;
        InterfaceInfo interfaceInfo = docScanner.lookupInterface(serviceClassName, operate, version);
        try {
            Class<? extends IRequest> requestClass = (Class<? extends IRequest>) Class.forName(interfaceInfo.getRequestClassName());
            IRequest bizRequest = gson.fromJson(bizRequestJson, requestClass);
            if (bizRequest instanceof LoginUserId){
                LoginUserId request0 = (LoginUserId)bizRequest;
                request0.setLoginUserId(authorityContext.getUserId());
            }
            if (bizRequest instanceof LoginUserName){
                LoginUserName request0 = (LoginUserName)bizRequest;
                request0.setLoginUserName(authorityContext.getUserName());
            }
            IResponse bizResponse = run(interfaceInfo, bizRequest);
            AjaxResponse ajaxResponse = AjaxResponse.builder()
                    .success()
                    .data(bizResponse)
                    .build();
            return gson.toJson(ajaxResponse);
        } catch (ClassNotFoundException e) {
            throw ErrorContextFactory.instance()
                    .message("not found class '{}'!", serviceClassName)
                    .solution("please check java package '{}' if exist class '{}'!", packageName, serviceClassName)
                    .runtimeException();
        } catch (Exception e) {
            log.error("happens unknown error!", e);
            AjaxResponse ajaxResponse = AjaxResponse.builder()
                    .failure()
                    .build();
            return gson.toJson(ajaxResponse);
        }
    }


    protected abstract IResponse run(InterfaceInfo interfaceInfo, IRequest request) throws Exception;
}



